package ran.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ran.constants.Constants;
import ran.domain.exam.dto.ExamAddDTO;
import ran.domain.exam.dto.ExamEditDTO;
import ran.domain.exam.dto.ExamQueryDTO;
import ran.domain.exam.dto.ExamQuestionAddDTO;
import ran.domain.exam.entity.Exam;
import ran.domain.exam.entity.ExamQuestion;
import ran.domain.exam.vo.ExamDetailVO;
import ran.domain.exam.vo.ExamVO;
import ran.domain.question.entity.Question;
import ran.domain.question.vo.QuestionVO;
import ran.enums.ResultCode;
import ran.exception.ServiceException;
import ran.manager.ExamRedisManager;
import ran.mapper.exam.ExamMapper;
import ran.mapper.exam.ExamQuestionMapper;
import ran.mapper.question.QuestionMapper;
import ran.service.exam.IExamService;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2025-10-23
 * Time: 18:08
 */
@Service
@Slf4j
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper, ExamQuestion> implements IExamService {
    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;

    @Autowired
    private ExamRedisManager examRedisManager;

    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        log.info("查询竞赛参数, examQueryDTO:{}",examQueryDTO);
        PageHelper.startPage(examQueryDTO.getPageNum(),examQueryDTO.getPageSize());// 引入分页插件,可以自动在查询条件下加入分页SQL语句
        return examMapper.selectExamList(examQueryDTO);
    }

    @Override
    public String add(ExamAddDTO examAddDTO) {
        checkExamParams(examAddDTO,null);
        Exam exam = new Exam();
        BeanUtil.copyProperties(examAddDTO,exam);
        examMapper.insert(exam);

        return exam.getExamId().toString();
    }







    private static List<ExamQuestion> getList(ExamQuestionAddDTO examQuestionAddDTO, LinkedHashSet<Long> questionIdSet) {
        List<ExamQuestion> list = new ArrayList<>();
        int count = 1;
        for (Long questionId : questionIdSet) {
            ExamQuestion examQuestion = new ExamQuestion();
            examQuestion.setQuestionId(questionId);
            examQuestion.setExamId(examQuestionAddDTO.getExamId());
            examQuestion.setQuestionOrder(count++);
            list.add(examQuestion);
        }
        return list;
    }

    @Override
    public ExamDetailVO detail(Long examId) {
        Exam exam = getExam(examId);
        ExamDetailVO examDetailVO = new ExamDetailVO();
        BeanUtil.copyProperties(exam,examDetailVO);
        List<ExamQuestion> examQuestionList = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                .select(ExamQuestion::getQuestionId)
                .eq(ExamQuestion::getExamId,examId));
        if (CollectionUtil.isEmpty(examQuestionList)) {
            // 空题目竞赛
            return examDetailVO;
        }
        // 有题目的竞赛
        List<Long> questionIds = examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();

        List<Question> questions = questionMapper.selectList(new LambdaQueryWrapper<Question>()
                .select(Question::getTitle, Question::getQuestionId, Question::getDifficulty)
                .in(Question::getQuestionId, questionIds));
        List<QuestionVO> questionVOList = BeanUtil.copyToList(questions, QuestionVO.class);
        examDetailVO.setQuestionVOList(questionVOList);
        return examDetailVO;
    }

    @Override
    public int edit(ExamEditDTO examEditDTO) {
        // 先检查是否存在
        Exam exam = getExam(examEditDTO.getExamId());
        // 竞赛发布不能进行操作
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_PUBLISHED);
        }
        // 检验是否开赛
        checkExam(exam);
        // 检查参数
        checkExamParams(examEditDTO,exam.getExamId());
        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        // 更新
        log.info("更新竞赛信息, exam:{}",exam);
        return examMapper.updateById(exam);
    }

    @Override
    public boolean questionAdd(ExamQuestionAddDTO examQuestionAddDTO) {
        // 检验竞赛是否存在或保存
        Exam exam = getExam(examQuestionAddDTO.getExamId());
        // 检验是否开赛
        checkExam(exam);
        // 竞赛发布不能进行操作
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_PUBLISHED);
        }
        LinkedHashSet<Long> questionIdSet = examQuestionAddDTO.getQuestionIdSet();
        if (CollectionUtil.isEmpty(questionIdSet)) {
            return true;
        }
        List<Question> questionList = questionMapper.selectBatchIds(questionIdSet);
        List<ExamQuestion> examQuestionList = examQuestionMapper.selectBatchIds(questionIdSet);
        if (CollectionUtil.isNotEmpty(examQuestionList)) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_EXISTS);
        }
        if (CollectionUtil.isEmpty(questionList) || questionList.size() != questionIdSet.size()) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }
        List<ExamQuestion> list = getList(examQuestionAddDTO, questionIdSet);
        return saveOrUpdateBatch(list);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        // 检验竞赛存在
        Exam exam = getExam(examId);
        // 检验是否开赛
        checkExam(exam);
        // 竞赛发布不能进行操作
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_PUBLISHED);
        }
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId)
                .eq(ExamQuestion::getQuestionId,questionId));
    }

    @Override
    public int delete(Long examId) {
        // 检验竞赛存在
        Exam exam = getExam(examId);
        // 竞赛发布不能进行操作
        if (Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_PUBLISHED);
        }
        // 检验是否开赛
        checkExam(exam);
        // 先删除竞赛中的题目
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>().eq(ExamQuestion::getExamId,examId));
        return examMapper.deleteById(examId);
    }

    @Override
    public int publish(Long examId) {
        // 检验竞赛存在
        Exam exam = getExam(examId);

        // 竞赛结束不能操作
        if(exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISHED);
        }

        // 检查竞赛是否有题目
        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        if (count == null || count <= 0) {
            throw new ServiceException(ResultCode.EXAM_NOT_QUESTION);
        }

        // 设置竞赛状态
        exam.setStatus(Constants.TRUE);

        // 先存储到Redis中 e:t:l -> 存id的list   和  e:d: -> 存竞赛详细信息
        examRedisManager.addCache(exam);

        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        // 检验竞赛存在
        Exam exam = getExam(examId);

        // 竞赛结束不能操作
        if(exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_FINISHED);
        }

        // 检验是否开赛
        checkExam(exam);

        // 设置竞赛状态
        exam.setStatus(Constants.FALSE);

        // 从Redis中删除, e:t:l -> 存竞赛id的list   和  e:d: -> 存竞赛详细信息
        // 从Redis中删除, e:q:l -> 存题目id的list
        examRedisManager.deleteCache(examId);
        
        return examMapper.updateById(exam);
    }

    private static void checkExam(Exam exam) {
        if (exam.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    private void checkExamParams(ExamAddDTO examAddDTO, Long examId) {
        // 检验是否重名
        List<Exam> list = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .eq(Exam::getTitle, examAddDTO.getTitle())
                .ne(examId != null,Exam::getExamId,examId));
        if (CollectionUtil.isNotEmpty(list)) {
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        // 检验竞赛开始时间和结束时间
        if (examAddDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_NOW_TIME);
        }
        if (examAddDTO.getStartTime().isAfter(examAddDTO.getEndTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }
    }

    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectOne(new LambdaQueryWrapper<Exam>().eq(Exam::getExamId,examId));
        if (exam == null) {
            throw new ServiceException(ResultCode.EXAM_NOT_EXISTS);
        }
        return exam;
    }
}
